/* ----------------------------------------------------------------------------
 * ssjs
 * ----------------------------------------------------------------------------
 * File Name    : smtp.js
 * Module       : smtp
 * Dependicies  : system, socket, base64
 * Description  : Implemets methods for Simple Mail Transfer Protocol
 * ----------------------------------------------------------------------------
 * $LastChangedBy$
 * $LastChangedDate$
 * $LastChangedRevision$
 * $HeadURL$
 * -------------------------------------------------------------------------- */
exports.smtp = function(args) {
    /* variables */
    var connection  = null,
        connected   = false,
        instance    = this,
        module      = instance, /* referencing to this for now. do not change */
        error       = [],
        response    = [],
        line_ending = "\r\n",
        line_length = 998,
        Base64      = require("base64");
        Socket      = require("socket").Socket;

    /* define settings */
    instance.settings = {
        debug   : 1,
        host    : "127.0.0.1",
        port    : 25,
        ssl     : false,
        ttl     : 30
    };

    /**
     * Performs SMTP authentication.  Must be run after running the
     * hello() method.  Returns true if successfully authenticated.
     * @access public
     * @return bool
     */
    module.authenticate = function (username, password) {
        /* check if connected */
        if (smtp_connected() == false) {
            error.push("Called authenticate() without being connected");

            if (instance.settings.debug >= 2) {
                system.stdout("Called authenticate() without being connected" + line_ending);
            }

            return false;
        }

        /* reply */
        var rply = null;

        /* start authentication */
        smtp_send("AUTH LOGIN");

        /* get reply */
        rply = smtp_receive();

        /* validate reply */
        if (!rply.match(/^334/)) {
            error.push(rply);

            if (instance.settings.debug >= 1) {
                system.stdout(rply);
            }

            return false;
        } else {
            if (instance.settings.debug >= 2) {
                system.stdout(rply);
            }
        }

        /* send encoded username */
        smtp_send_encoded(username);

        /* get reply */
        rply = smtp_receive();

        /* validate reply */
        if (!rply.match(/^334/)) {
            error.push(rply);

            if (instance.settings.debug >= 1) {
                system.stdout(rply);
            }

            return false;
        } else {
            if (instance.settings.debug >= 2) {
                system.stdout(rply);
            }
        }

        /* send encoded password */
        smtp_send_encoded(password);

        /* get reply */
        rply = smtp_receive();

        /* validate reply */
        if (!rply.match(/^235/)) {
            error.push(rply);

            if (instance.settings.debug >= 1) {
                system.stdout(rply);
            }

            return false;
        } else {
            if (instance.settings.debug >= 2) {
                system.stdout(rply);
            }
        }

        return true;
    };

    /**
     * Connect to the server specified on the port specified.
     * If the port is not specified use the default SMTP_PORT.
     * If tval is specified then a connection will try and be
     * established with the server for that number of seconds.
     * If tval is not specified the default is 30 seconds to
     * try on the connection.
     *
     * SMTP CODE SUCCESS: 220
     * SMTP CODE FAILURE: 421
     * @access public
     * @return bool
     */
    module.connect = function(host, port, ttl) {
        if (smtp_connected()) {
            error.push("Already connected to server");

            if (instance.settings.debug >= 2) {
                system.stdout("Already connected to server" + line_ending);
            }

            return false;
        }

        if (typeof host != 'undefined') {
            instance.settings.host = host;
        }

        if (typeof port != 'undefined') {
            instance.settings.port = port;
        }

        if (typeof ttl != 'undefined') {
            instance.settings.ttl = ttl;
        }

        try {
            connection = new Socket(Socket.PF_INET, Socket.SOCK_STREAM, Socket.IPPROTO_TCP);
            connected = connection.connect(instance.settings.host, instance.settings.port);

            /* check if there is valid response from server */
            if (smtp_connected()) {
                /* get reply */
                var rply = smtp_receive();

                /* validate reply */
                if (!rply.match(/^220/)) {
                    connected = false;

                    error.push('Client connection was not accepted. Blocked either by IP or connection limit');

                    if (instance.settings.debug >= 1) {
                        system.stdout('Client connection was not accepted. Blocked either by IP or connection limit' + line_ending);
                    }

                    return false;
                }

                if (instance.settings.debug >= 2) {
                    system.stdout(rply);
                }
            }

        } catch (e) {
            connected = false;
            error.push(e.toString());

            if (instance.settings.debug >= 1) {
                system.stdout(e.toString() + line_ending);
            }

            return false;
        }

        return true;
    };

    /**
     * Issues a data command and sends the msg_data to the server
     * finializing the mail transaction. $msg_data is the message
     * that is to be send with the headers. Each header needs to be
     * on a single line followed by a <CRLF> with the message headers
     * and the message body being seperated by and additional <CRLF>.
     *
     * Implements rfc 821: DATA <CRLF>
     *
     * SMTP CODE INTERMEDIATE: 354
     * [data]
     * <CRLF>.<CRLF>
     * SMTP CODE SUCCESS: 250
     * SMTP CODE FAILURE: 552,554,451,452
     * SMTP CODE FAILURE: 451,554
     * SMTP CODE ERROR  : 500,501,503,421
     * @access public
     * @return bool
     */
    module.data = function(data) {
        if (smtp_connected() == false) {
            error.push("Called data() without being connected");

            if (instance.settings.debug >= 2) {
                system.stdout("Called data() without being connected" + line_ending);
            }

            return false;
        }

        /* reply */
        var rply = null;

        /* send 'DATA' to server */
        smtp_send("DATA");

        /* get reply */
        rply = smtp_receive();

        /* validate reply */
        if (!rply.match(/^354/)) {
            error.push(rply);

            if (instance.settings.debug >= 1) {
                system.stdout(rply);
            }

            return false
        } else {
            if (instance.settings.debug >= 2) {
                system.stdout(rply);
            }
        }

        /* the server is ready to accept data! */
        /* according to rfc 821 we should not send more than 1000 */
        /* including the CRLF */
        /* characters on a single line so we will break the data up */
        /* into lines by \r and/or \n then if needed we will break */
        /* each of those into smaller lines to fit within the limit. */
        /* in addition we will be looking for lines that start with */
        /* a period '.' and append and additional period '.' to that */
        /* line. NOTE: this does not count towards are limit. */
        /* normalize the line breaks so we know the explode works */
        data = data.replace(/(\r\n|\n)/, '\n').split('\n');

        /* we need to find a good way to determine is headers are */
        /* in the msg_data or if it is a straight msg body */
        /* currently I'm assuming rfc 822 definitions of msg headers */
        /* and if the first field of the first line (':' sperated) */
        /* does not contain a space then it _should_ be a header */
        /* and we can process all lines before a blank "" line as */
        /* headers. */
        var in_headers = data[0].match(/:/) ? true : false;

        for (var ndx = 0; ndx < data.length; ++ndx) {
            /* formatted data */
            var data_out = [];

            /* check if we have headers in first line */
            if (data[ndx] == '' && in_headers) {
                in_headers = false;
            }

            /* let's break the lines into smaller pieces */
            while (data[ndx].length > line_length) {
                var position = data[ndx].substr(0, line_length).lastIndexOf(' ');

                /* patch to DOS attack */
                if (!position) {
                    position = line_length - 1;
                }

                /* split data into chunks */
                data_out.push(data[ndx].substr(0, position));
                data[ndx] = data[ndx].substr(position + 1);

                /* if we are processing headers we need to */
                /* add a LWSP-char to the front of the new line */
                /* rfc 822 on long msg headers */
                if (in_headers) {
                    data[ndx] = '\t' + data[ndx];
                }
            }

            /* put rest of the data into output */
            data_out.push(data[ndx]);

            /* now send the data to server */
            for(var mdx = 0; mdx < data_out.length; ++mdx) {
                if (data_out[mdx].length > 0) {
                    data_out[mdx] = '.' + data_out[mdx];
                }

                smtp_send(data_out[mdx]);
            }
        }

        /* message transfer is completed */
        smtp_send(line_ending + '.');

        /* get rply */
        var rply = smtp_receive();

        /* validate reply */
        if (!rply.match(/^250/)) {
            error.push(rply);

            if (instance.settings.debug >= 2) {
                system.stdout(rply);
            }

            return false
        } else {
            if (instance.settings.debug >= 3) {
                system.stdout(rply);
            }
        }

        return true;
    };

    /**
     * Closes the socket and cleans up the state of the class.
     * It is not considered good to use this function without
     * first trying to use QUIT.
     * @access public
     * @return void
     */
    module.disconnect = function() {
        if (smtp_connected() == false) {
            error.push("Called disconnect() without being connected");

            if (instance.settings.debug >= 2) {
                system.stdout("Called disconnect() without being connected");
            }

            return false;
        }

        connection.close();
        connected = false;
    };

    /**
     * Expand takes the name and asks the server to list all the
     * people who are members of the _list_. Expand will return
     * back and array of the result or false if an error occurs.
     * Each value in the array returned has the format of:
     * [ <full-name> <sp> ] <path>
     * The definition of <path> is defined in rfc 821
     *
     * Implements rfc 821: EXPN <SP> <string> <CRLF>
     *
     * SMTP CODE SUCCESS: 250
     * SMTP CODE FAILURE: 550
     * SMTP CODE ERROR  : 500,501,502,504,421
     * @access public
     * @return string array
     */
    module.expand = function(name) {
        if (smtp_connected() == false) {
            error.push("Called expand() without being connected");

            if (instance.settings.debug >= 2) {
                system.stdout("Called expand() without being connected" + line_ending);
            }

            return false;
        }

        if (typeof name == 'undefined' || name == null) {
            error.push('smtp.expand -> name cannot be null');

            if (instance.settings.debug >= 2) {
                system.stdout('smtp.expand -> name cannot be null' + line_ending);
            }

            return false;
        }

        /* reply */
        var rply = null;

        /* send 'EXPN' to server */
        smtp_send("EXPN " + name);

        /* get reply */
        rply = smtp_receive();

        /* validate reply */
        if (!rply.match(/^250/)) {
            error.push(rply);

            if (instance.settings.debug >= 2) {
                system.stdout(rply);
            }

            return false
        } else {
            if (instance.settings.debug >= 3) {
                system.stdout(rply);
            }
        }

        return true;

    };

    /**
     * Sends the HELO command to the smtp server.
     * This makes sure that we and the server are in
     * the same known state.
     *
     * Implements from rfc 821: HELO <SP> <domain> <CRLF>
     *
     * SMTP CODE SUCCESS: 250
     * SMTP CODE ERROR  : 500, 501, 504, 421
     * @access public
     * @return bool */
    module.hello = function(host) {
        if (smtp_connected() == false) {
            error.push("Called hello() without being connected");

            if (instance.settings.debug >= 2) {
                system.stdout("Called hello() without being connected" + line_ending);
            }
            return false;
        }

        /* if a host for HELO isn't specified determine a suitable one */
        if (typeof host == 'undefined') {
            host = Socket.getHostName();
        }

        /* Send extended hello first (RFC 2821) */
        if (smtp_send_hello('EHLO', host) == false) {
            if (smtp_send_hello('HELO', host) == false) {
                return false;
            }
        }

        return true;
    };

    /**
     * Gets help information on the keyword specified. If the keyword
     * is not specified then returns generic help, ussually contianing
     * A list of keywords that help is available on. This function
     * returns the results back to the user. It is up to the user to
     * handle the returned data. If an error occurs then false is
     * returned with $this->error set appropiately.
     *
     * Implements rfc 821: HELP [ <SP> <string> ] <CRLF>
     *
     * SMTP CODE SUCCESS: 211,214
     * SMTP CODE ERROR  : 500,501,502,504,421
     * @access public
     * @return string
     */
    module.help = function(keyword) {
        if (smtp_connected() == false) {
            error.push("Called help() without being connected");

            if (instance.settings.debug >= 2) {
                system.stdout("Called help() without being connected" + line_ending);
            }

            return false;
        }

        /* reply */
        var rply = null;

        /* send 'HELP' to server */
        if (typeof keyword == 'undefined') {
            smtp_send("HELP");
        } else {
            smtp_send("HELP " + keyword);
        }

        /* get reply */
        rply = smtp_receive();

        /* validate reply */
        if (!rply.match(/^(211|214)/)) {
            error.push(rply);

            if (instance.settings.debug >= 2) {
                system.stdout(rply);
            }

            return false
        } else {
            if (instance.settings.debug >= 3) {
                system.stdout(rply);
            }
        }

        return true;
    };

    /**
     * Starts a mail transaction from the email address specified in
     * $from. Returns true if successful or false otherwise. If True
     * the mail transaction is started and then one or more Recipient
     * commands may be called followed by a Data command.
     *
     * Implements rfc 821: MAIL <SP> FROM:<reverse-path> <CRLF>
     *
     * SMTP CODE SUCCESS: 250
     * SMTP CODE SUCCESS: 552,451,452
     * SMTP CODE SUCCESS: 500,501,421
     * @access public
     * @return bool
     */
    module.mail = function(from) {
        if (smtp_connected() == false) {
            error.push("Called mail() without being connected");

            if (instance.settings.debug >= 2) {
                system.stdout("Called mail() without being connected" + line_ending);
            }

            return false;
        }

        /* reply */
        var rply = null;

        /* send 'MAIL FROM' to server */
        smtp_send("MAIL FROM:<" + from + ">");

        /* get reply */
        rply = smtp_receive();

        /* validate reply */
        if (!rply.match(/^250/)) {
            error.push(rply);

            if (instance.settings.debug >= 2) {
                system.stdout(rply);
            }

            return false
        } else {
            if (instance.settings.debug >= 3) {
                system.stdout(rply);
            }
        }

        return true;
    };

    /**
     * Sends the command NOOP to the SMTP server.
     *
     * Implements from rfc 821: NOOP <CRLF>
     *
     * SMTP CODE SUCCESS: 250
     * SMTP CODE ERROR  : 500, 421
     * @access public
     * @return bool
     */
    module.noop = function() {
        if (smtp_connected() == false) {
            error.push("Called noop() without being connected");

            if (instance.settings.debug >= 2) {
                system.stdout("Called noop() without being connected" + line_ending);
            }

            return false;
        }

        /* reply */
        var rply = null;

        /* send 'NOOP' to server */
        smtp_send("NOOP");

        /* get reply */
        rply = smtp_receive();

        /* validate reply */
        if (!rply.match(/^250/)) {
            error.push(rply);

            if (instance.settings.debug >= 2) {
                system.stdout(rply);
            }

            return false
        } else {
            if (instance.settings.debug >= 3) {
                system.stdout(rply);
            }
        }

        return true;
    };

    /**
     * Sends the quit command to the server and then closes the socket
     * if there is no error or the close_on_error argument is true.
     *
     * Implements from rfc 821: QUIT <CRLF>
     *
     * SMTP CODE SUCCESS: 221
     * SMTP CODE ERROR  : 500
     * @access public
     * @return bool
     */
    module.quit = function(close_on_error) {
        if (smtp_connected() == false) {
            error.push("Called quit() without being connected");

            if (instance.settings.debug >= 2) {
                system.stdout("Called quit() without being connected" + line_ending);
            }

            return false;
        }

        if (typeof close_on_error == 'undefined') {
            close_on_error = true;
        }

        /* reply */
        var rply = null;

        /* send 'QUIT' to server */
        smtp_send("QUIT");

        /* get reply */
        rply = smtp_receive();

        /* validate reply */
        if (!rply.match(/^221/)) {
            error.push(rply);

            if (instance.settings.debug >= 2) {
                system.stdout(rply);
            }

            if (close_on_error) {
                error.push('Closing connection due to error');

                if (instance.settings.debug >= 1) {
                    system.stdout('Closing connection due to error' + line_ending);
                }

                module.disconnect();
            }

            return false
        } else {
            if (instance.settings.debug >= 3) {
                system.stdout(rply);
            }
        }

        return true;
    };

    /**
     * Sends the command RCPT to the SMTP server with the TO: argument of $to.
     * Returns true if the recipient was accepted false if it was rejected.
     *
     * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF>
     *
     * SMTP CODE SUCCESS: 250,251
     * SMTP CODE FAILURE: 550,551,552,553,450,451,452
     * SMTP CODE ERROR  : 500,501,503,421
     * @access public
     * @return bool */
    module.recipient = function(to) {
        if (smtp_connected() == false) {
            error.push("Called recipient() without being connected");

            if (instance.settings.debug >= 2) {
                system.stdout("Called recipient() without being connected" + line_ending);
            }

            return false;
        }

        /* reply */
        var rply = null;

        /* send 'RCPT TO' to server */
        smtp_send("RCPT TO:<" + to + ">");

        /* get reply */
        rply = smtp_receive();

        /* validate reply */
        if (!rply.match(/^(250|251)/)) {
            error.push(rply);

            if (instance.settings.debug >= 2) {
                system.stdout(rply);
            }

            return false
        } else {
            if (instance.settings.debug >= 3) {
                system.stdout(rply);
            }
        }

        return true;
    };

    /**
     * Sends the RSET command to abort and transaction that is
     * currently in progress. Returns true if successful false
     * otherwise.
     *
     * Implements rfc 821: RSET <CRLF>
     *
     * SMTP CODE SUCCESS: 250
     * SMTP CODE ERROR  : 500,501,504,421
     * @access public
     * @return bool
     */
    module.reset = function() {
        if (smtp_connected() == false) {
            error.push("Called reset() without being connected");

            if (instance.settings.debug >= 2) {
                system.stdout("Called reset() without being connected" + line_ending);
            }

            return false;
        }

        /* reply */
        var rply = null;

        /* send 'RSET' to server */
        smtp_send("RSET");

        /* get reply */
        rply = smtp_receive();

        /* validate reply */
        if (!rply.match(/^250/)) {
            error.push(rply);

            if (instance.settings.debug >= 2) {
                system.stdout(rply);
            }

            return false
        } else {
            if (instance.settings.debug >= 3) {
                system.stdout(rply);
            }
        }

        return true;
    };

    /**
     * Starts a mail transaction from the email address specified in
     * $from. Returns true if successful or false otherwise. If True
     * the mail transaction is started and then one or more Recipient
     * commands may be called followed by a Data command. This command
     * will send the message to the users terminal if they are logged
     * in.
     *
     * Implements rfc 821: SEND <SP> FROM:<reverse-path> <CRLF>
     *
     * SMTP CODE SUCCESS: 250
     * SMTP CODE SUCCESS: 552,451,452
     * SMTP CODE SUCCESS: 500,501,502,421
     * @access public
     * @return bool
     */
    module.send = function(from) {
        if (smtp_connected() == false) {
            error.push("Called send() without being connected");

            if (instance.settings.debug >= 2) {
                system.stdout("Called send() without being connected" + line_ending);
            }

            return false;
        }

        /* reply */
        var rply = null;

        /* send 'SEND <SP> FROM:<reverse-path> ' to server */
        smtp_send("SEND FROM:<" + from + ">");

        /* get reply */
        rply = smtp_receive();

        /* validate reply */
        if (!rply.match(/^250/)) {
            error.push(rply);

            if (instance.settings.debug >= 2) {
                system.stdout(rply);
            }

            return false
        } else {
            if (instance.settings.debug >= 3) {
                system.stdout(rply);
            }
        }

        return true;
    };

    /**
     * Starts a mail transaction from the email address specified in
     * $from. Returns true if successful or false otherwise. If True
     * the mail transaction is started and then one or more Recipient
     * commands may be called followed by a Data command. This command
     * will send the message to the users terminal if they are logged
     * in and send them an email.
     *
     * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF>
     *
     * SMTP CODE SUCCESS: 250
     * SMTP CODE SUCCESS: 552,451,452
     * SMTP CODE SUCCESS: 500,501,502,421
     * @access public
     * @return bool
     */
    module.sendandmail = function() {
        if (smtp_connected() == false) {
            error.push("Called sendandmail() without being connected");

            if (instance.settings.debug >= 2) {
                system.stdout("Called sendandmail() without being connected" + line_ending);
            }

            return false;
        }

        /* reply */
        var rply = null;

        /* send 'SAML <SP> FROM:<reverse-path> ' to server */
        smtp_send("SAML FROM:<" + from + ">");

        /* get reply */
        rply = smtp_receive();

        /* validate reply */
        if (!rply.match(/^250/)) {
            error.push(rply);

            if (instance.settings.debug >= 2) {
                system.stdout(rply);
            }

            return false
        } else {
            if (instance.settings.debug >= 3) {
                system.stdout(rply);
            }
        }

        return true;
    };

    /**
     * Starts a mail transaction from the email address specified in
     * $from. Returns true if successful or false otherwise. If True
     * the mail transaction is started and then one or more Recipient
     * commands may be called followed by a Data command. This command
     * will send the message to the users terminal if they are logged
     * in or mail it to them if they are not.
     *
     * Implements rfc 821: SOML <SP> FROM:<reverse-path> <CRLF>
     *
     * SMTP CODE SUCCESS: 250
     * SMTP CODE SUCCESS: 552,451,452
     * SMTP CODE SUCCESS: 500,501,502,421
     * @access public
     * @return bool
     */
    module.sendormail = function() {
        if (smtp_connected() == false) {
            error.push("Called sendormail() without being connected");

            if (instance.settings.debug >= 2) {
                system.stdout("Called sendormail() without being connected" + line_ending);
            }

            return false;
        }

        /* reply */
        var rply = null;

        /* send 'SOML <SP> FROM:<reverse-path> ' to server */
        smtp_send("SOML FROM:<" + from + ">");

        /* get reply */
        rply = smtp_receive();

        /* validate reply */
        if (!rply.match(/^250/)) {
            error.push(rply);

            if (instance.settings.debug >= 2) {
                system.stdout(rply);
            }

            return false
        } else {
            if (instance.settings.debug >= 3) {
                system.stdout(rply);
            }
        }

        return true;
    };

    /**
     * This is an optional command for SMTP that this class does not
     * support. This method is here to make the RFC821 Definition
     * complete for this class and __may__ be implimented in the future
     *
     * Implements from rfc 821: TURN <CRLF>
     *
     * SMTP CODE SUCCESS: 250
     * SMTP CODE FAILURE: 502
     * SMTP CODE ERROR  : 500, 503
     * @access public
     * @return bool
     */
    module.turn = function() {
        error.push("TURN method of SMTP is not implimented. see: RFC2821");

        if (instance.settings.debug >= 1) {
            system.stdout("TURN method of SMTP is not implimented. see: RFC2821" + line_ending);
        }

        return false;
    };

    /**
     * Verifies that the name is recognized by the server.
     * Returns false if the name could not be verified otherwise
     * the response from the server is returned.
     *
     * Implements rfc 821: VRFY <SP> <string> <CRLF>
     *
     * SMTP CODE SUCCESS: 250,251
     * SMTP CODE FAILURE: 550,551,553
     * SMTP CODE ERROR  : 500,501,502,421
     * @access public
     * @return bool
     */
    module.verify = function(name) {
        if (smtp_connected() == false) {
            error.push("Called verify() without being connected");

            if (instance.settings.debug >= 2) {
                system.stdout("Called verify() without being connected" + line_ending);
            }

            return false;
        }

        /* reply */
        var rply = null;

        /* send 'VRFY' to server */
        smtp_send("VRFY " + name);

        /* get reply */
        rply = smtp_receive();

        /* validate reply */
        if (!rply.match(/^(250|251)/)) {
            error.push(rply);

            if (instance.settings.debug >= 2) {
                system.stdout(rply);
            }

            return false
        } else {
            if (instance.settings.debug >= 3) {
                system.stdout(rply);
            }
        }

        return true;
    };

    /**
     * Gets errors
     * @access public
     * @return array
     * */
    module.get_errors = function() {
        return error;
    };

    /**
     * Gets all data received from server
     * @access public
     * @return array
     */
    module.get_response = function() {
        return response;
    }

    /**
     * Returns true if connected to a server otherwise false
     * @access private
     * @return bool
     */
    var smtp_connected = function() {
        return connected;
    };

    /**
     * Sends plain string to server
     * @param string str
     * @access private
     * @return void
     * */
    var smtp_send = function(data) {
        if (smtp_connected() == false) {
            error.push('Not connected to server');

            if (instance.settings.debug >= 1) {
                system.stdout('Not connected to server' + line_ending);
            }

            return false;
        }

        connection.send(data);
        connection.send(line_ending);

        if (instance.settings.debug >= 3) {
            system.stdout("SEND =>" + data + line_ending);
        }
    };

    /**
     * Sends base64 encoded string to server
     * @param string str
     * @access private
     * @return void
     * */
    var smtp_send_encoded = function(str) {
        smtp_send(Base64.encode(str));
    };

    /**
     * Sends a HELO/EHLO command.
     * @access private
     * @return bool
     */
    var smtp_send_hello = function(hello, host) {
        smtp_send(hello + ' ' + host);

        /* check if server supports Extened SMTP */
        var rply = smtp_receive();

        if (rply.match(/^250/)) {
            if (instance.settings.debug >= 2) {
                system.stdout(rply);
            }

            return true;
        } else {
            error.push(hello + ' not accepted from server');

            if (instance.settings.debug >= 1) {
                system.stdout(hello + ' not accepted from server' + line_ending);
            }

            return false;
        }
    };

    /**
     * Receives the output from server.
     * returns the last received line
     * @access private
     * @return string
     * */
    var smtp_receive = function() {
        var rply = connection.receive(1024).toString('utf-8');

        response.push(rply);

        return rply;
    };

    /* extend settings */
    for (var arg in args) {
        instance.settings[arg] = args[arg];
    }

    return instance;
}
